// 06_references_and_slices.rs
// 演示 Rust 中的引用和切片概念

// 将原来的 main 函数改为公共的 run 函数
pub fn run() {
    // 1. 引用的回顾
    // 引用允许我们使用值但不获取其所有权
    let s1 = String::from("hello");
    let len = calculate_length(&s1); // 传递不可变引用
    println!("字符串 '{}' 的长度是 {}", s1, len);
    
    // 2. 可变引用
    let mut s2 = String::from("hello");
    change(&mut s2); // 传递可变引用
    println!("修改后的字符串: {}", s2);
    
    // 3. 切片（Slices）
    // 切片是对集合中连续元素序列的引用，它没有所有权
    
    // a. 字符串切片
    let s3 = String::from("hello world");
    let hello = &s3[0..5]; // 从索引0开始到索引5（不包含5）的切片
    let world = &s3[6..11]; // 从索引6开始到索引11（不包含11）的切片
    
    println!("字符串切片1: {}, 字符串切片2: {}", hello, world);
    
    // 简化的切片语法
    let hello2 = &s3[..5]; // 从开始到索引5
    let world2 = &s3[6..]; // 从索引6到结束
    let whole = &s3[..]; // 整个字符串
    
    println!("简化切片1: {}, 简化切片2: {}, 整个字符串切片: {}", hello2, world2, whole);
    
    // b. 字符串字面量是切片
    // 字符串字面量的类型是 &str，它是一个指向二进制程序特定位置的切片
    let literal = "hello rust";
    // literal 的类型是 &str，它是一个不可变引用
    println!("字符串字面量: {}", literal);
    
    // c. 将字符串切片作为参数
    let s4 = String::from("hello world");
    let word = first_word(&s4); // 传递 &String 给接收 &str 的函数
    println!("第一个单词: {}", word);
    
    // 也可以直接传递字符串切片
    let s5 = "hello rust";
    let word2 = first_word(s5); // s5 已经是 &str 类型
    println!("第一个单词: {}", word2);
    
    // d. 字符串切片与所有权
    let mut s6 = String::from("hello world");
    let word3 = first_word(&s6); // 获取第一个单词的切片
    
    // 以下操作会导致编译错误，因为我们有一个对 s6 的不可变引用
    // 同时又想修改 s6
    // s6.clear(); // 取消注释这行会导致编译错误
    
    println!("第一个单词: {}", word3);
    
    // 4. 其他类型的切片
    // 除了字符串切片，还可以创建其他类型的切片
    let a = [1, 2, 3, 4, 5];
    let slice = &a[1..3]; // 数组切片，类型是 &[i32]
    
    println!("数组切片: {:?}", slice);
    
    // 验证切片中的元素
    assert_eq!(slice, &[2, 3]);
    
    // 5. 切片是动态大小的类型
    // 切片本身不存储长度信息，而是存储了指向数据起始位置的指针和长度
    // 这使得切片可以在编译时不知道具体长度的情况下安全使用
    
    // 6. 可变切片
    let mut b = [1, 2, 3, 4, 5];
    let mut_slice = &mut b[1..4]; // 创建可变切片
    
    // 修改可变切片中的元素
    for element in mut_slice.iter_mut() {
        *element *= 2;
    }
    
    println!("修改后的数组: {:?}", b);
    
    // 7. 更复杂的切片示例：找到字符串中的第一个单词
    let s7 = String::from("  hello   rust  programming  ");
    let trimmed = s7.trim(); // 去除首尾空白
    let first = first_word(trimmed);
    println!("原始字符串: '{}'", s7);
    println!("去除空白后: '{}'", trimmed);
    println!("第一个单词: '{}'", first);
    
    // 8. 使用函数返回切片
    let s8 = String::from("hello beautiful world");
    let second_word = second_word(&s8);
    println!("第二个单词: {}", second_word);
}

// 计算字符串长度（使用不可变引用）
fn calculate_length(s: &String) -> usize {
    s.len()
}

// 修改字符串（使用可变引用）
fn change(s: &mut String) {
    s.push_str(", world");
}

// 找到字符串中的第一个单词（返回字符串切片）
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes(); // 将字符串转换为字节数组
    
    // 迭代字节数组，查找空格
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i]; // 找到空格，返回从开始到空格前的切片
        }
    }
    
    &s[..] // 如果没有找到空格，返回整个字符串的切片
}

// 找到字符串中的第二个单词
fn second_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    let mut first_space = None;
    
    // 找到第一个空格
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            first_space = Some(i);
            break;
        }
    }
    
    // 如果没有找到空格，返回空字符串
    if first_space.is_none() {
        return "";
    }
    
    let first_space_pos = first_space.unwrap() + 1;
    
    // 从第一个空格之后开始查找第二个空格或字符串结束
    for (i, &item) in bytes[first_space_pos..].iter().enumerate() {
        if item == b' ' {
            return &s[first_space_pos..first_space_pos + i];
        }
    }
    
    // 如果没有找到第二个空格，返回从第一个空格后到结束的切片
    &s[first_space_pos..]
}